package schedulo.core.task.provider;

import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import schedulo.core.task.Task;
import schedulo.core.task.filter.TaskFilter;
import schedulo.core.task.provider.parameters.Parameters;

/**
 * A TaskProvider is an entity that provide access to a task list, the task list
 * can be either local FileTaskProvider, distant PHPTaskProvider, unknown
 * DBTaskProvider
 * 
 * @author topaz
 * 
 */
public abstract class TaskProvider {

	private String name;

	private Spec providerSpec;

	private LinkedList<TaskProviderListener> listeners;

	public TaskProvider(Spec providerSpec) {
		this.providerSpec = providerSpec;
		listeners = new LinkedList<TaskProviderListener>();
		setName(providerSpec.getParameters().getArgString("name",
				providerSpec.toString()));
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void addTaskProviderListener(TaskProviderListener listener) {
		listeners.add(listener);
	}

	public void removeTaskProviderListener(TaskProviderListener listener) {
		listeners.remove(listener);
	}

	protected void fireTaskProviderUpdated() {
		Iterator<TaskProviderListener> itListeners = listeners.iterator();
		while (itListeners.hasNext()) {
			itListeners.next().taskProviderUpdated(this);
		}
	}

	public Spec getSpec() {
		return providerSpec;
	}

	public Parameters getParameters() {
		return providerSpec.getParameters();
	}

	public String toString() {
		if (name == null) {
			return providerSpec.toString();
		}
		return name;
	}

	/**
	 * Force the provider to flush its internal caches and to reload tasks from
	 * the underlaying provider.
	 * 
	 */
	public abstract void invalidate();

	/**
	 * retrieves the task list held by this provider.
	 * 
	 * @return
	 * @throws IOException
	 */
	public abstract List<Task> getTaskListImpl() throws IOException;

	/**
	 * update the task list held by this provider.
	 * 
	 * @param taskCollection
	 * @throws IOException
	 */
	public abstract void setTaskListImpl(List<Task> taskList)
			throws IOException;

	/**
	 * ask the provider to store the previously set task list
	 * 
	 * @throws IOException
	 */
	public abstract void save() throws IOException;

	/**
	 * filter the task of this provider against a list of filters
	 *
	 * NOTE: override this function if the provider implements a faster filter method (i.e. database)
	 * @param filters
	 * @return
	 * @throws IOException
	 */
	public List<Task> applyFilters(List<TaskFilter> filters) throws IOException {
		if (filters == null || filters.size() == 0) {
			return getTaskList();
		}
		List<Task> list = getTaskList();
		if (list == null || list.size() == 0) {
			return null;
		}

		// iterate filters
		Iterator<TaskFilter> itFilters = filters.iterator();
		while (itFilters.hasNext()) {
			TaskFilter f = itFilters.next();

			// iterate tasks,
			// task is iterated internally to optimize number of check when task
			// filtered with previous filter used
			list = f.filter(list);
		}
		return list;
	}

	/**
	 * Remove the provider from the task provider list
	 * 
	 */
	public void dispose() {
		TaskProviderFactory.getInstance().removeTaskProvider(this);
	}

	private List<Task> taskCache;

	public List<Task> getTaskList() throws IOException {
		taskCache = getTaskListImpl();
		setAsTaskProvider(taskCache);
		return taskCache;
	}

	public void setTaskList(List<Task> taskList) throws IOException {
		setTaskListImpl(taskList);
		if (taskCache != null) {
			setAsTaskProvider(taskCache);
		}
		taskCache = taskList;
		setAsTaskProvider(taskList);
	}

	protected void setAsTaskProvider(List<Task> tasks) {
		if (tasks == null) {
			return;
		}
		Iterator<Task> it = tasks.iterator();
		while (it.hasNext()) {
			it.next().setTaskProvider(this);
		}
	}
}
